
#include "modbusserialtest.h"

#include <QFile>
#include <QFileInfo>
#include <QJsonObject>
#include <QCryptographicHash>
#include <QSerialPortInfo>
#include <QSettings>

ModbusSerialTest::ModbusSerialTest(QObject *parent)
    : QObject(parent)
{
    // 初始化定时器
    connect(&timer, &QTimer::timeout, this, &ModbusSerialTest::onTimeout);

    // 连接信号和槽
    connect(&serialPort, &QSerialPort::readyRead, this, &ModbusSerialTest::onReadyRead);
    connect(&serialPort, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::errorOccurred),
            this, &ModbusSerialTest::onErrorOccurred);
}

ModbusSerialTest::~ModbusSerialTest()
{
    // 清理资源
    closePort();
}

void ModbusSerialTest::scanSerialPorts()
{
    // 扫描所有可用的串口
    QList<QSerialPortInfo> serialPorts = QSerialPortInfo::availablePorts();

    // 遍历串口列表并打印信息
    foreach (const QSerialPortInfo &serialPort, serialPorts) {
        qDebug() << "Name        :" << serialPort.portName();
        qDebug() << "Description :" << serialPort.description();
        qDebug() << "Manufacturer:" << serialPort.manufacturer();
        qDebug() << "System Location:" << serialPort.systemLocation();
        qDebug() << "Vendor Identifier:" << serialPort.vendorIdentifier();
        qDebug() << "Product Identifier:" << serialPort.productIdentifier();
        qDebug() << "Serial Number :" << serialPort.serialNumber();
        qDebug() << "-----------------";
    }
}


bool ModbusSerialTest::startPort()
{
    QString portName = "plcModbousSerialtest";

    QSettings settings("./config/systemConfig.ini", QSettings::IniFormat);
    settings.beginGroup("system");
    QString pName = settings.value(portName).toString();
    settings.endGroup();

    // 检查端口是否已经打开
    if (!serialPort.isOpen()) {
        qDebug() << QString("正在打开PLC串口 %1...\n").arg(pName);
    } else {
        qDebug() << QString("%1已打开，已连接成功.\n").arg(pName);
        return true;
    }

    // 初始化串口
    serialPort.setPortName(pName); // 或者其他端口，根据实际情况设置
    serialPort.setBaudRate(QSerialPort::Baud19200);
    serialPort.setDataBits(QSerialPort::Data8);
    serialPort.setParity(QSerialPort::EvenParity);
    serialPort.setStopBits(QSerialPort::OneStop);
    serialPort.setFlowControl(QSerialPort::NoFlowControl);

    if (!serialPort.open(QIODevice::ReadWrite)) {
        qDebug() << QString("错误 %1 串口打开失败！").arg(serialPort.portName());
        return false;
    }

    // 开始周期性地检查PLC状态
    // ...
    timer.start(INTERVAL); // 每秒检查一次PLC状态

    plcConnectState = true;
    return true;
}

void ModbusSerialTest::stopPort()
{
    timer.stop();
}

void ModbusSerialTest::closePort()
{
    stopPort();
    serialPort.clear();
    serialPort.close();

    plcConnectState = false;
}

char ModbusSerialTest::get_deviceAddress()
{
    return static_cast<char>(deviceAddress);
}

void ModbusSerialTest::onReadyRead()
{
    readBuffer.clear();
    readBuffer = readBuffer_Temp.append(serialPort.readAll());
    readBuffer_Temp.clear();

    // 缓冲区是否有数据
    if (!readBuffer.isEmpty())
    {
        qDebug() << QString("Revc(%1):").arg(readBuffer.size()) << toHexString(readBuffer);

        // 解析数据帧
        if(parseModbusResponseTest(readBuffer))
        {
            sendCount = 1;

//            qDebug() << "数据帧格式正确.\n";
        } else {
            qDebug() << "数据帧格式错误.\n";
        }

    }
}


bool ModbusSerialTest::checkConnectState()
{
    if(sendCount++ >= packetLoss || sendCount == 0)
    {
        plcConnectState = false;
        timer.stop();
    } else {
        plcConnectState = true;
        timer.start(INTERVAL); // 每秒检查一次PLC状态
    }

    return plcConnectState;
}


void ModbusSerialTest::onTimeout()
{
    static uint8_t read_time = 18;

    ++read_time %= 20;

    // 分频，每50ms进一次定时器，每1s发送一次读寄存器数据
    if(!read_time)
    {
        sendModbusRequest(0x03, 0x00, 0x04);// 获取PLC状态寄存器、拍照寄存器
        sendModbusRequest(0x02, 0x5000, 32);// 获取PLC传感器状态
    }

    // 有数据就发送
    if (!m_dataQueue.isEmpty()) {
        if(serialPort.isOpen())
        {

            QByteArray dataToSend = m_dataQueue.dequeue();
            // 发送一帧数据到串口
            serialPort.write(dataToSend);
            // 发送计数
            sendCount++;

        } else {
            qDebug() << "串口打开失败，发送失败！";
        }
    }
}


QString ModbusSerialTest::toHexString(const QByteArray &byteArray)
{
    QString hexString;
    for (const char &byte : byteArray)
    {
        hexString.append(QString("%1 ").arg((quint8)byte, 2, 16, QLatin1Char('0')).toUpper());
    }
    return hexString;
}


// 发送Modbus请求
void ModbusSerialTest::sendModbusRequest(uint8_t functionCode, uint16_t address, uint16_t value)
{
    // 添加到数据帧
    QByteArray request = constructModbusRequest(functionCode, address, value);

    // 添加到发送队列
    m_dataQueue.enqueue(request);

    // qDebug 打印发送信息
    qDebug() << "Sent Modbus request:" << toHexString(request);
}

// 发送modbus请求
void ModbusSerialTest::do_setSendModbusRequest(uint8_t functionCode, uint16_t address, uint16_t value)
{
    sendModbusRequest(functionCode, address, value);
}

// 发送脉冲信号
void ModbusSerialTest::do_plcSendPulse(uint8_t functionCode, uint16_t address)
{
    sendModbusRequest(functionCode, address, 0xFF00);
    sendModbusRequest(functionCode, address, 0x00);
}

// 构造Modbus RTU请求帧
QByteArray ModbusSerialTest::constructModbusRequest(uint8_t functionCode, uint16_t address, uint16_t value)
{
    QByteArray request;
    request.append(static_cast<char>(get_deviceAddress())); // 设备地址
    request.append(static_cast<char>(functionCode)); // 功能码
    request.append(static_cast<char>((address >> 8) & 0xFF)); // 寄存器地址（高字节）
    request.append(static_cast<char>(address & 0xFF)); // 寄存器地址（低字节）
    request.append(static_cast<char>((value >> 8) & 0xFF));  // 写入数据内容（高字节）
    request.append(static_cast<char>(value & 0xFF));         // 写入数据内容（低字节）

//    qDebug() << "计算CRC:" << request.left(request.size()).toHex().toUpper();

    // 计算CRC并添加到请求帧末尾
    QPair<uint8_t, uint8_t> crc = calculateCRC(request.left(request.size()));
    request.append(static_cast<char>(crc.second));
    request.append(static_cast<char>(crc.first));
    return request;
}

//CRC16计算函数
uint16_t ModbusSerialTest::ModbusCRC16(const QByteArray &senddata)
{
    int len=senddata.size();
    uint16_t wcrc=0XFFFF;//预置16位crc寄存器，初值全部为1
    uint8_t temp;//定义中间变量
    int i=0,j=0;//定义计数
    for(i=0;i<len;i++)//循环计算每个数据
    {
       temp=senddata.at(i);
       wcrc^=temp;
       for(j=0;j<8;j++){
          //判断右移出的是不是1，如果是1则与多项式进行异或。
          if(wcrc&0X0001){
              wcrc>>=1;//先将数据右移一位
              wcrc^=0XA001;//与上面的多项式进行异或
          }
          else//如果不是1，则直接移出
              wcrc>>=1;//直接移出
       }
    }
    temp=wcrc;//crc的值
    return wcrc;
}


// 计算Modbus RTU CRC校验
QPair<uint8_t, uint8_t> ModbusSerialTest::calculateCRC(const QByteArray &data)
{
    int len=data.size();
    uint16_t wcrc=0XFFFF;//预置16位crc寄存器，初值全部为1
    uint8_t temp;//定义中间变量
    int i=0,j=0;//定义计数
    for(i=0;i<len;i++)//循环计算每个数据
    {
       temp=data.at(i);
       wcrc^=temp;
       for(j=0;j<8;j++){
          //判断右移出的是不是1，如果是1则与多项式进行异或。
          if(wcrc&0X0001){
              wcrc>>=1;//先将数据右移一位
              wcrc^=0XA001;//与上面的多项式进行异或
          }
          else//如果不是1，则直接移出
              wcrc>>=1;//直接移出
       }
    }
    temp=wcrc;//crc的值

    return {static_cast<uint8_t>(wcrc >> 8), static_cast<uint8_t>(wcrc & 0xFF)};
}


QPair<uint8_t, uint8_t> ModbusSerialTest::getHighAndLowByte(const QString &str, const QByteArray &byteArray, const uint8_t index)
{
    if(byteArray.size() < 2 )
    {
        qDebug() << "数据少于两个，获取高低位失败";
    }

    if( index >= byteArray.size())
    {
        qDebug() << "指针下标越界，获取高低位失败";
    }

    // 获取状态位数据
    uint8_t high_byte = static_cast<uint8_t>(byteArray.at(index - 1));
    uint8_t low_byte = static_cast<uint8_t>(byteArray.at(index));
//    uint16_t data = static_cast<uint8_t>(((high_byte << 8) & 0xFF00) + (low_byte & 0xFF));

//    qDebug() << QString("%1_high_byte: ").arg(str) << high_byte << QString("%1_low_byte: ").arg(str) << low_byte << " data: " << data;

    return {high_byte, low_byte};
}

uint16_t ModbusSerialTest::getHighAddLowUint16_t(const QString &str, const QByteArray &byteArray, const uint8_t index)
{
    if(byteArray.size() < 2 )
    {
        qDebug() << "数据少于两个，获取高低位失败";
    }

    if( index >= byteArray.size())
    {
        qDebug() << "指针下标越界，获取高低位失败";
    }

    // 获取状态位数据
    uint8_t high_byte = static_cast<uint8_t>(byteArray.at(index - 1));
    uint8_t low_byte = static_cast<uint8_t>(byteArray.at(index));
    uint16_t data = static_cast<uint16_t>(((high_byte << 8) & 0xFF00) + (low_byte & 0xFF));

//    qDebug() << QString("%1_high_byte: ").arg(str) << high_byte << QString("%1_low_byte: ").arg(str) << low_byte << " data: " << data;

    return data;
}

QString ModbusSerialTest::ensureThreeDigits(const QString &input) {
    // 使用正则表达式匹配数字
    QRegExp numberRx("\\d+");
    if (numberRx.indexIn(input) != -1) {
        QString numberStr = numberRx.cap(0); // 提取匹配到的数字
        int number = numberStr.toInt(); // 将字符串转换为整数
        // 使用QString的arg函数来格式化数字，确保至少三位，不足则补零
        return QString("%1").arg(number, 3, 10, QChar('0'));
    }
    // 如果输入字符串中没有数字，返回原始字符串或空字符串，根据你的需求
    return input; // 或者 return QString();
}


bool ModbusSerialTest::getPlcConnectState() const
{
    return plcConnectState;
}

void ModbusSerialTest::setPlcConnectState(bool value)
{
    plcConnectState = value;
}

bool ModbusSerialTest::parseModbusResponseTest(const QByteArray &response)
{
    int currentIndex = 0; // 初始化当前下标为0

    uint8_t len = 8; // 默认读寄存器响应帧

    while((response.size() - currentIndex) > len-2)
    {
//        qDebug() << "解析数据帧第" << currentIndex+1  << "个字节...";
        // 485地址
        if(static_cast<uint8_t>(response.at(currentIndex)) == get_deviceAddress())
        {
            uint8_t index = static_cast<uint8_t>(response.at(currentIndex));
            uint8_t size = static_cast<uint8_t>(response.size());

            if(index >= size){
                qDebug() << "-->指针越界访问!!!";
            }

            // 取功能码
            uint8_t functionCode = static_cast<uint8_t>(response.at(currentIndex+1));
            // 功能码    读寄存器响应、写寄存器响应、读线圈响应、写线圈响应
            if(0x03 == functionCode || 0x06 == functionCode || 0x01 == functionCode || 0x02 == functionCode || 0x05 == functionCode)
            {
                if(0x06 == functionCode || 0x05 == functionCode)
                {
                    len = 8;// 限制写操作返回数据帧长度为8
                } else if(0x01 == functionCode || 0x02 == functionCode)
                {
                    if(0x02 == functionCode) len = 9;
                    else len = 6;
                } else if(0x03 == functionCode){
//                    if(read_3functionCodeFlag == true)
//                    {
                        len = 13;
//                    } else {
//                        len = 7;
//                    }

                }

                qDebug()<< "格式帧长度：" << len << " 接收帧长度：" << response.size();
                if(len > response.size())
                {
                    qDebug() << "长度错误，丢弃这帧数据.";
                    break;// 跳出循环
                }

                // 获取一帧数据
                QByteArray oneFrameData = response.mid(currentIndex, len);
                // 取CRC校验位比对
                QPair<uint8_t, uint8_t> calculatedCrc = calculateCRC(response.mid(currentIndex, len-2));

                if (static_cast<uint8_t>(oneFrameData.at(len - 1)) == (calculatedCrc.first) && static_cast<uint8_t>(oneFrameData.at(len - 2)) == (calculatedCrc.second))
                {
                    // CRC校验正确
                    qDebug() << "CRC校验通过，进行功能码判断...  " << "功能码：" << functionCode;

                    uint32_t byteNum = oneFrameData.at(2);
                    // 处理读取到的数据
                    if (functionCode == 0x03)
                    { // 读取保持寄存器响应
//                        if(byteNum == 1)// 读plc配方编号
//                        {
//                            // 获取状态位数据
//                            uint16_t devGroup = getHighAddLowUint16_t("", oneFrameData, len-2-1);

//                            emit sendDevCurrentGroup(devGroup);

//                        } else if(byteNum == 4)// 读状态位、拍照寄存器
//                        {
                            // 获取状态位数据
                            uint16_t topPhotoRequestState = getHighAddLowUint16_t("topPhotoRequest", oneFrameData, len-2*4-1);

                            // 获取状态位数据
                            uint16_t ABCD_PhotoRequestState = getHighAddLowUint16_t("ABCD_PhotoRequest", oneFrameData, len-2*3-1);

                            // 获取状态位数据
                            uint16_t bottomPhotoRequestState = getHighAddLowUint16_t("bottomPhotoRequest", oneFrameData, len-2*2-1);

                            // 获取状态位数据
                            uint16_t state = getHighAddLowUint16_t("state", oneFrameData, len-2-1);


                            qDebug() << "top:" << topPhotoRequestState << "ABCD:" << ABCD_PhotoRequestState << "bottom:" << bottomPhotoRequestState << "state:" << state;

                            // 更新最新状态位显示框 发送信号
                            emit newStatusBit(state);

                            // 更新顶部拍照状态位 发送信号
                            emit topPhotoRequest(topPhotoRequestState);


                            // 更新侧边拍照状态位 发送信号
                            emit ABCD_PhotoRequest(ABCD_PhotoRequestState);


                            // 更新底部拍照状态位 发送信号
                            emit bottomPhotoRequest(bottomPhotoRequestState);

                            plcState = state;

//                        }
                    }
                    else if(functionCode == 0x06)
                    {
                        // 获取状态位数据
                        uint16_t data = getHighAddLowUint16_t("data", oneFrameData, len-2-1);
                        // 获取地址数据
                        uint16_t address = getHighAddLowUint16_t("address", oneFrameData, len-2*2-1);

                        if(address == 0x00)// 顶部拍照
                        {
                            // PLC 顶部拍照准备就绪，拍照请求
                            if(data == 0x01)
                            {
                                qDebug() << "请求 顶部拍照";
                            } else if(data == 0x02) // 写入PLC顶部拍照指令
                            {
                                if(plcState == 7)
                                {
                                    // 写入成功
                                    qDebug() << "完成 顶部拍照";
                                }
                            }
                        } else if(address == 0x01)// 旋转 A、B、C、D面拍照请求
                        {
                            if(data == 0x01)
                            {
                                if(plcState == 9)// A
                                {
                                    qDebug() << "请求 A面拍照";
                                } else if(plcState == 11)// B
                                {
                                    qDebug() << "请求 B面拍照";
                                } else if(plcState == 14)// C
                                {
                                    qDebug() << "请求 C面拍照";
                                } else if(plcState == 20)// D
                                {
                                    qDebug() << "请求 D面拍照";
                                }
                            } else if(data == 0x02)
                            {
                                if(plcState == 11)// A
                                {
                                    qDebug() << "完成 A面拍照";

                                } else if(plcState == 14)// B
                                {
                                    qDebug() << "完成 B面拍照";

                                } else if(plcState == 17)// C
                                {
                                    qDebug() << "完成 C面拍照";

                                } else if(plcState == 20)// D
                                {
                                    qDebug() << "完成 D面拍照";

                                }
                            }
                        } else if(address == 0x02)// 底部拍照请求
                        {
                            if(data == 0x01)
                            {

                                qDebug() << "请求 底部拍照";
                            } else if(data == 0x02)
                            {
                                if(plcState == 26)
                                {
                                    qDebug() << "完成 底部拍照";
                                }
                            }
                        }
                    }
                    else if(0x05 == functionCode)
                    {
                        // 获取状态位数据
                        uint16_t data = getHighAddLowUint16_t("data", oneFrameData, len-2-1);
                        // 获取地址数据
                        uint16_t address = getHighAddLowUint16_t("address", oneFrameData, len-2*2-1);

//                        emit shiftOnAStatus();
                    }
                    else if(0x01 == functionCode || 0x02 == functionCode)
                    {
                        uint8_t len = oneFrameData.at(2);
                        QByteArray data_32bit = oneFrameData.mid(3, len);


                        qDebug() << "接收到线圈传感器数据：" << toHexString(data_32bit);

                        uint8_t data_x7_x0 = data_32bit.at(0);
                        uint8_t data_x17_x10 = data_32bit.at(1);
                        uint8_t data_x27_x20 = data_32bit.at(2);
                        uint8_t data_x37_x30 = data_32bit.at(3);

//                        qDebug() << "输入口检测信号：" << ((data_x17_x10 >> 7) & 0x01);
//                        qDebug() << "顶升气缸正限位：" << (data_x17_x10 & 0x01);
//                        qDebug() << "调宽气缸负限位：" << ((data_x17_x10 >> 0x02) & 0x01);
//                        qDebug() << "复位X07："      << ((data_x27_x20 >> 0x07) & 0x01);
//                        qDebug() << "急停X06："      << ((data_x27_x20 >> 0x06) & 0x01);
//                        qDebug() << "输出口光栅X05：" << ((data_x27_x20 >> 0x05) & 0x01);
//                        qDebug() << "输出口监测X04：" << ((data_x27_x20 >> 0x04) & 0x01);
//                        qDebug() << "B工位B监测信号X03：" << ((data_x27_x20 >> 0x03) & 0x01);
//                        qDebug() << "B工位A监测信号X02：" << ((data_x27_x20 >> 0x02) & 0x01);
//                        qDebug() << "A工位B监测信号X01：" << ((data_x27_x20 >> 0x01) & 0x01);
//                        qDebug() << "A工位A监测信号X00：" << ((data_x27_x20 >> 0x00) & 0x01);
//                        qDebug() << "0x07-0x01：" << data_x7_x0;
//                        qDebug() << "输出口检测信号：" << ((data_x27_x20 >> 3) & 0x01);

                        QMap<QString, uint8_t> sensorFlagMap;

                        sensorFlagMap.insert("输入口检测信号", static_cast<uint8_t>((data_x17_x10 >> 7) & 0x01));
                        sensorFlagMap.insert("顶升气缸正限位", static_cast<uint8_t>(data_x17_x10 & 0x01));
                        sensorFlagMap.insert("调宽气缸负限位", static_cast<uint8_t>((data_x17_x10 >> 2) & 0x01));
                        sensorFlagMap.insert("旋转轴原点", static_cast<uint8_t>((data_x7_x0 >> 3) & 0x01));
                        sensorFlagMap.insert("输出口检测信号", static_cast<uint8_t>((data_x27_x20 >> 4) & 0x01));
                        sensorFlagMap.insert("复位", static_cast<uint8_t>((data_x27_x20 >> 7) & 0x01));

                        emit sensorStateMap(sensorFlagMap);

//                        qDebug() << "sensorFlagMap:" << sensorFlagMap;
                    }
                    return true;
                } else {
                    QString str_frame_crc_low =  QString("%1 ").arg(static_cast<uint8_t>(oneFrameData.at(len - 2)), 2, 16, QLatin1Char('0')).toUpper();
                    QString str_frame_crc_high = QString("%1 ").arg(static_cast<uint8_t>(oneFrameData.at(len - 1)), 2, 16, QLatin1Char('0')).toUpper();

                    QString str_Crc_low =  QString("%1 ").arg((calculatedCrc.second), 2, 16, QLatin1Char('0')).toUpper();
                    QString str_Crc_high = QString("%1 ").arg((calculatedCrc.first), 2, 16, QLatin1Char('0')).toUpper();

                    qDebug() << "低字节：" << str_frame_crc_low << "\t" << str_Crc_low;
                    qDebug() << "高字节：" << str_frame_crc_high << "\t" << str_Crc_high;
                    qDebug() << "CRC校验码错误.\n";
                }
            } else {
                qDebug() << "功能码错误.\n";
            }
        }
        currentIndex++;
    }
    return false;
}


void ModbusSerialTest::onErrorOccurred(QSerialPort::SerialPortError error)
{
    // 使用qDebug记录错误信息
    switch(error) {
    case QSerialPort::NoError:
        // 这通常不会在这里发生，因为NoError表示没有错误
        qDebug() << "No error occurred.";
        break;
    case QSerialPort::DeviceNotFoundError:
        qDebug() << "Error: The serial port could not be found.";
        break;
    case QSerialPort::PermissionError:
        qDebug() << "Error: Permission to access the serial port was denied.";
        break;
    case QSerialPort::OpenError:
        qDebug() << "Error: An error occurred while opening the serial port.";
        break;
    case QSerialPort::ReadError:
        qDebug() << "Error: An error occurred while reading the serial port.";
        break;
    case QSerialPort::WriteError:
        qDebug() << "Error: An error occurred while writing to the serial port.";
        break;
    case QSerialPort::ResourceError:
        qDebug() << "Error: An invalid resource error occurred.";
        break;
    case QSerialPort::UnsupportedOperationError:
        qDebug() << "Error: The operation is not supported.";
        break;
    case QSerialPort::TimeoutError:
        qDebug() << "Error: A timeout occurred.";
        // 这里你可以考虑重新尝试连接或者采取其他恢复措施
        break;
    case QSerialPort::UnknownError:
        qDebug() << "Error: An unknown error occurred.";
        break;
    default:
        qDebug() << "Unknown error code received.";
    }

    // 在这里，你还可以采取其他措施，比如尝试重新连接串口，
    // 或者向用户显示错误消息等。

    // 例如，重新打开串口
     if (error == QSerialPort::TimeoutError) {
         serialPort.close();
         // 等待一段时间后再次尝试打开串口
         // ...
     }
}

// 添加数据帧到发送队列
void ModbusSerialTest::do_enqueueData(const QByteArray &data)
{
    m_dataQueue.enqueue(data);
}
